MongoDB 架构与高级特性
本文档介绍 MongoDB 的存储引擎、副本集和分片集群架构。
存储引擎
MongoDB 默认使用 WiredTiger 存储引擎(3.2+),提供:
- 文档级锁:高并发写入性能
- 压缩:支持 snappy 和 zlib 压缩
- 检查点:定期将数据持久化到磁盘
- Journaling:写操作日志,保证数据安全
yaml
# mongod.conf 配置
storage:
engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 4 # 缓存大小
journalCompressor: snappy
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true副本集(Replica Set)
副本集是一组维护相同数据集的 MongoDB 实例,提供数据冗余和高可用性。
架构图
code
┌─────────────────────────────────────┐
│ 副本集架构 │
└─────────────────────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Primary │───────▶│ Secondary │───────▶│ Secondary │
│ (主节点) │ │ (从节点) │ │ (从节点) │
│ 读写操作 │ │ 只读 │ │ 只读 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ 异步复制 │
└───────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Arbiter (仲裁节点) │
│ 不存储数据,仅参与投票,节省资源 │
└─────────────────────────────────────────────────────────────────┘副本集成员角色
| 角色 | 描述 | 功能 |
|---|---|---|
| Primary | 主节点 | 处理所有写操作,默认读操作 |
| Secondary | 从节点 | 复制主节点数据,可处理读请求 |
| Arbiter | 仲裁节点 | 参与选举投票,不存储数据 |
| Hidden | 隐藏节点 | 对客户端不可见,用于备份/报表 |
| Delayed | 延迟节点 | 数据延迟同步,用于灾难恢复 |
副本集配置
javascript
// 初始化副本集
rs.initiate({
_id: "myReplicaSet",
members: [
{ _id: 0, host: "mongo1:27017", priority: 2 },
{ _id: 1, host: "mongo2:27017", priority: 1 },
{ _id: 2, host: "mongo3:27017", priority: 1 },
{ _id: 3, host: "mongo4:27017", arbiterOnly: true }
]
})
// 查看副本集状态
rs.status()
// 查看配置
rs.conf()
// 添加成员
rs.add("mongo5:27017")
// 添加仲裁节点
rs.addArb("mongo6:27017")
// 移除成员
rs.remove("mongo5:27017")
// 配置隐藏节点
rs.add({
host: "mongo-hidden:27017",
priority: 0,
hidden: true
})
// 配置延迟节点
rs.add({
host: "mongo-delayed:27017",
priority: 0,
hidden: true,
slaveDelay: 3600 // 延迟 1 小时
})读写关注
javascript
// 写关注(Write Concern)
db.orders.insertOne(
{ item: "book", qty: 10 },
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
)
// w: "majority" - 大多数节点确认
// j: true - 等待 journal 刷盘
// wtimeout: 超时时间
// 读关注(Read Concern)
db.orders.find().readConcern("majority")
// 读偏好(Read Preference)
// primary - 只从主节点读(默认)
// primaryPreferred - 优先主节点
// secondary - 只从从节点读
// secondaryPreferred - 优先从节点
// nearest - 最近节点
db.orders.find().readPref("secondaryPreferred")连接副本集
javascript
// Node.js 连接副本集
const uri = "mongodb://mongo1:27017,mongo2:27017,mongo3:27017/mydb?replicaSet=myReplicaSet"
const client = new MongoClient(uri, {
readPreference: "secondaryPreferred",
readConcernLevel: "majority",
w: "majority"
})故障转移
code
故障转移流程:
1. 主节点故障
2. 从节点检测心跳超时
3. 发起选举
4. 获得多数票的从节点成为新主节点
5. 客户端自动重连到新主节点
选举条件:
- 需要获得多数成员投票
- 至少需要 3 个成员才能自动故障转移
- 优先级最高的节点优先成为主节点分片集群(Sharding)
分片允许将数据分布在多个服务器上,实现水平扩展。
架构图
code
┌─────────────────────────────────────────────────────────────────────────┐
│ 分片集群架构 │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ mongos (路由节点) │
│ 接收查询,路由到正确的分片 │
└─────────────────────────────────────────────────────────────────────────┘
│
│ 查询路由
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (副本集) │ │ (副本集) │ │ (副本集) │
│ 范围: min-100 │ │ 范围: 100-200 │ │ 范围: 200-max │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Config Servers (配置服务器) │
│ 存储集群元数据和配置信息 │
│ (必须是副本集) │
└─────────────────────────────────────────────────────────────────────────┘组件说明
| 组件 | 数量 | 描述 |
|---|---|---|
| mongos | 1+ | 路由进程,客户端连接入口 |
| Config Servers | 3 | 存储集群元数据,必须是副本集 |
| Shard | 1+ | 存储数据,推荐使用副本集 |
分片策略
| 策略 | 描述 | 适用场景 |
|---|---|---|
| 范围分片 | 按分片键值范围划分 | 范围查询频繁 |
| 哈希分片 | 按分片键哈希值划分 | 数据均匀分布,随机访问 |
| 标签分片 | 自定义数据分布规则 | 地理分布、多租户 |
配置分片
bash
# 1. 启动配置服务器(必须是副本集)
mongod --configsvr --replSet configRs --port 27019
# 初始化配置服务器副本集
mongosh --port 27019
rs.initiate({ _id: "configRs", configsvr: true, members: [...] })bash
# 2. 启动 mongos 路由
mongos --configdb configRs/mongo-cfg1:27019,mongo-cfg2:27019,mongo-cfg3:27019javascript
// 3. 添加分片
sh.addShard("shard1/mongo-s1a:27017,mongo-s1b:27017")
sh.addShard("shard2/mongo-s2a:27017,mongo-s2b:27017")
// 4. 启用数据库分片
sh.enableSharding("mydb")
// 5. 对集合分片
// 范围分片
sh.shardCollection("mydb.users", { username: 1 })
// 哈希分片
sh.shardCollection("mydb.logs", { timestamp: "hashed" })
// 查看分片状态
sh.status()
// 查看分片分布
db.users.getShardDistribution()标签分片
javascript
// 添加标签
sh.addShardTag("shard1", "US")
sh.addShardTag("shard2", "EU")
sh.addShardTag("shard3", "APAC")
// 配置标签范围
sh.addTagRange(
"mydb.users",
{ region: "US" },
{ region: "US" },
"US"
)
// 启用均衡器
sh.enableBalancing("mydb.users")
sh.startBalancer()分片键选择原则
- 高基数:分片键值多样性高,避免数据倾斜
- 高频率查询:选择常用查询字段
- 非单调递增:避免热点问题
- 避免低基数:如性别、状态等字段
code
好的分片键示例:
- { userId: 1 } - 高基数,常用查询
- { _id: "hashed" } - 均匀分布
- { createdAt: 1, userId: 1 } - 复合分片键
不好的分片键示例:
- { timestamp: 1 } - 单调递增,产生热点
- { status: 1 } - 低基数,数据倾斜数据均衡
javascript
// 查看均衡状态
sh.getBalancerState()
// 手动迁移块
sh.moveChunk("mydb.users", { username: "alice" }, "shard2")
// 拆分块
sh.splitAt("mydb.users", { username: "m" })
// 关闭均衡器(维护期间)
sh.stopBalancer()
sh.startBalancer()性能考量
副本集性能
- 写操作需要复制到多数节点
- 从节点读取可能存在延迟
- 合理配置 priority 影响选举
分片集群性能
- 分片键决定数据分布
- 广播查询(无分片键)需要查询所有分片
- 跨分片事务性能较低
上一篇:[Node.js 操作](3-Node.js 操作.md)
下一篇:运维管理